package com.linq.cool.beauty.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.linq.cool.api.domain.BeautyMember;
import com.linq.cool.api.domain.BeautyMemberItem;
import com.linq.cool.api.dto.BeautyMemberBindDTO;
import com.linq.cool.api.dto.BeautyMemeberRegisterDTO;
import com.linq.cool.api.vo.WeChatLoginUser;
import com.linq.cool.beauty.domain.BeautyRefund;
import com.linq.cool.beauty.domain.BeautyTopUp;
import com.linq.cool.beauty.dto.*;
import com.linq.cool.beauty.mapper.BeautyMemberMapper;
import com.linq.cool.beauty.service.BeautyMemberItemService;
import com.linq.cool.beauty.service.BeautyMemberService;
import com.linq.cool.beauty.service.BeautyRefundService;
import com.linq.cool.beauty.service.BeautyTopUpService;
import com.linq.cool.common.constants.CacheConstants;
import com.linq.cool.common.constants.Constants;
import com.linq.cool.common.constants.UserConstants;
import com.linq.cool.common.core.exception.BusinessException;
import com.linq.cool.common.core.utils.BeanUtils;
import com.linq.cool.common.core.utils.IdWorker;
import com.linq.cool.common.core.utils.SecurityUtils;
import com.linq.cool.common.redis.service.RedisService;
import com.linq.cool.common.security.service.WeChatTokenService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;

/**
 * @Author: yqlin
 * @Date: 2021/1/30 21:16
 * @Description:
 * @Version: 1.0.0
 */
@Slf4j
@Service
public class BeautyMemberServiceImpl extends ServiceImpl<BeautyMemberMapper, BeautyMember> implements BeautyMemberService {
    @Autowired
    private WeChatTokenService weChatTokenService;
    @Autowired
    private RedisService redisService;
    @Autowired
    private IdWorker idWorker;
    @Autowired
    private BeautyTopUpService beautyTopUpService;
    @Autowired
    private BeautyRefundService beautyRefundService;
    @Autowired
    private BeautyMemberItemService beautyMemberItemService;

    @Override
    public BeautyMember getByOpenId(String openId) {
        return this.getOne(new LambdaQueryWrapper<BeautyMember>()
                                   .eq(BeautyMember::getIsDeleted, UserConstants.StateEnum.STATUS_NORMAL.getCode())
                                   .eq(BeautyMember::getStatus, UserConstants.StateEnum.STATUS_NORMAL.getCode())
                                   .eq(BeautyMember::getOpenId, openId)
        );
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public BeautyMember bineMember(BeautyMemberBindDTO dto) {
        // 校验手机号是否存在 并且openId不为空
        if (this.checkPhoneUsed(dto.getPhone())) {
            throw new BusinessException("用户已存在");
        }
        // 获取当前登录用户，获取到openid，根据openid查询用户，设置 手机号、密码
        WeChatLoginUser loginUser = redisService.getCacheObject(CacheConstants.LOGIN_TOKEN_KEY + dto.getToken());
        String openId = loginUser.getOpenId();
        if (this.checkOpenIdBinded(dto.getPhone())) {
            throw new BusinessException("该手机号已经被绑定了");
        }
        // id  avatarUrl    gender     nickName  openId
        BeautyMember byOpenId = this.getByOpenId(openId);
        // 判断此手机号是否存在 如果存在并且openid为空 就用查询到的绑定
        // 存在的话就代表是人工去创建的账号
        BeautyMember byPhone = this.getByPhone(dto.getPhone());
        if (byPhone != null) {
            if (StringUtils.checkValNull(byPhone.getOpenId())) {
                byPhone.setOpenId(byOpenId.getOpenId())
                        .setAvatar(byOpenId.getAvatar())
                        .setNickname(byOpenId.getNickname())
                        .setPassword(SecurityUtils.encryptPassword(dto.getPassword()));
                this.updateById(byPhone);
                // 删除 之前创建的微信账号
                this.removeById(byOpenId.getId());
                log.info("NEW_CUSTOMER--byPhone:{}", byPhone);
                weChatTokenService.removeLoginUser(loginUser.getToken());
                return byPhone;
            }
        } else {
            // 不是人工创建的
            byOpenId.setPhone(dto.getPhone()).setPassword(SecurityUtils.encryptPassword(dto.getPassword()));
            this.updateById(byOpenId);
            log.info("NEW_MEMBER--byOpenId:{}", byOpenId);
            weChatTokenService.removeLoginUser(loginUser.getToken());
            return byOpenId;
        }
        return null;
    }


    @Override
    public IPage<BeautyMember> listByPage(IPage<BeautyMember> page, BeautyMemberQueryDTO dto) {
        return this.page(page, this.getLambdaQueryWrapper(dto));
    }

    @Override
    public boolean saveMember(BeautyMemberDTO dto) {
        BeautyMember member = new BeautyMember();
        BeanUtils.copyProperties(dto, member);
        member.setPassword(SecurityUtils.encryptPassword(dto.getPassword()));
        member.setId(idWorker.nextId());
        // 判断手机号是否唯一
        if (this.checkPhoneUnique(member)) {
            throw new BusinessException("新增客户'" + member.getPhone() + "'失败，手机号码已存在");
        }
        // 判断身份证号是否唯义
        if (this.checkIdNumberUnique(member)) {
            throw new BusinessException("新增客户'" + member.getIdNumber() + "'失败，身份证号码已存在");
        }
        member.setCreator(SecurityUtils.getUserId());
        return this.save(member);
    }

    @Override
    public boolean updateMember(BeautyMemberDTO dto) {
        BeautyMember member = new BeautyMember();
        BeanUtils.copyProperties(dto, member);
        // 查询当前客户
        BeautyMember currentMemeber = this.getById(dto.getId());
        // 我们的密码不能修改
        member.setPassword(currentMemeber.getPassword());
        // 判断手机号是否唯一
        if (this.checkPhoneUnique(member)) {
            throw new BusinessException("修改客户'" + member.getPhone() + "'失败，手机号码已存在");
        }
        // 判断身份证号是否唯义
        if (this.checkIdNumberUnique(member)) {
            throw new BusinessException("修改客户'" + member.getIdNumber() + "'失败，身份证号码已存在");
        }
        member.setModifier(SecurityUtils.getUserId()).setGmtModified(new Date());
        return this.updateById(member);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean topUpWithCash(BeautyMemberTopUpDTO dto) {
        // 客户更改
        BeautyMember member = this.getById(dto.getMemberId());
        member.setAmount(member.getAmount().add(dto.getAmount()))
                .setHistoryAmount(member.getHistoryAmount().add(dto.getAmount()))
                .setModifier(SecurityUtils.getUserId()).setGmtModified(new Date());
        this.updateById(member);
        // 充值记录保存
        BeautyTopUp topUpLog = new BeautyTopUp();
        topUpLog.setId(idWorker.nextId() + "")
                .setMemberRealName(member.getRealName())
                .setMemberPhone(member.getPhone())
                .setMemberId(dto.getMemberId())
                .setPayType(UserConstants.StateEnum.PAY_TYPE_CASH.getCode())
                .setAmount(dto.getAmount())
                .setRestAmount(dto.getAmount())
                // 设置支付时间
                .setPayTime(new Date())
                // 已支付
                .setStatus(UserConstants.StateEnum.ORDER_STATUS_1.getCode())
                .setModifier(SecurityUtils.getUserId()).setGmtModified(new Date());
        beautyTopUpService.save(topUpLog);
        // 查询客户字表信息 如果第一次充值 就是保存 否则就是更新
        BeautyMemberItem memberItem = beautyMemberItemService.getOne(new LambdaQueryWrapper<BeautyMemberItem>()
                                                                             .eq(BeautyMemberItem::getMemberId, member.getId())
        );
        // 第一次充值
        if (null == memberItem) {
            log.info("{}第一次充值", member.getPhone());
            memberItem = new BeautyMemberItem();
            // 保存客户子表信息
            memberItem
                    .setId(idWorker.nextId())
                    .setMemberId(member.getId())
                    .setTopUpCount(1)
                    .setMemberRealName(member.getRealName())
                    .setMemberPhone(member.getPhone())
                    // 累计消费
                    .setConsumeAmount(topUpLog.getAmount())
                    // 累计充值
                    .setTopUpAmount(topUpLog.getAmount())
                    .setCreator(SecurityUtils.getUserId())
            ;
            member.setCreator(SecurityUtils.getUserId());
            return beautyMemberItemService.save(memberItem);
        } else {
            // 更新
            memberItem
                    .setTopUpCount(memberItem.getTopUpCount() + 1)
                    .setTopUpAmount(memberItem.getTopUpAmount().add(topUpLog.getAmount()))
                    .setConsumeAmount(memberItem.getConsumeAmount().add(topUpLog.getAmount()))
                    .setModifier(SecurityUtils.getUserId()).setModifier(member.getModifier());
            return beautyMemberItemService.updateById(memberItem);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean refundWithCash(BeautyRefundDTO dto) {
        // 先判断充值订单是否已经变成已退费状态 如果已经变成了 就不执行下面的操作了
        BeautyTopUp preTopUp = beautyTopUpService.getById(dto.getTopUpId());
        if (null == preTopUp) {
            throw new BusinessException(String.format("充值订单号[%s]的之前没有收费记录，不能使用支付宝退费，请核对后再查询", dto.getTopUpId()));
        }
        if (UserConstants.StateEnum.ORDER_STATUS_4.getCode().equals(preTopUp.getStatus())) {
            throw new BusinessException("该订单已退费");
        }
        // 判断之前是否使用支付宝支付
        if (!UserConstants.StateEnum.PAY_TYPE_CASH.getCode().equals(preTopUp.getPayType())) {
            throw new BusinessException(String.format("订单号[%s]的之前的支付方式为支付宝或微信，不能使用现金退费，请核对后再查询", dto.getTopUpId()));
        }
        // a<b, 返回-1
        // a=b，返回0
        // a>b, 返回1
        // 与未退费金额比较
        if (dto.getAmount().compareTo(preTopUp.getRestAmount()) > 0) {
            throw new BusinessException("退费金额超过充值金额");
        }
        // 保存退费订单
        BeautyRefund refund = new BeautyRefund();
        final String refundId = idWorker.nextId() + "";
        refund.setId(refundId)
                .setAmount(dto.getAmount())
                .setTopUpId(preTopUp.getId())
                .setMemberId(preTopUp.getMemberId())
                .setMemberPhone(preTopUp.getMemberPhone())
                .setMemberRealName(preTopUp.getMemberRealName())
                // 设置退费类型
                .setBackType(UserConstants.StateEnum.PAY_TYPE_CASH.getCode())
                // 设置退费时间
                .setBackTime(new Date())
                // 修改订单状态
                .setStatus(UserConstants.StateEnum.ORDER_REFUND_STATUS_1.getCode())
                .setCreator(SecurityUtils.getUserId());
        // 判断退费订单是全额退款还是部分退款  未退费金额和充值金额必须相等的时候才是 全额退款
        //  原来的充值订单也要进行相应字段的更新 rest_amount 以及充值订单的状态
        if (dto.getAmount().compareTo(preTopUp.getAmount()) == 0 && dto.getAmount().compareTo(preTopUp.getRestAmount()) == 0) {
            // 全额
            refund.setType(UserConstants.StateEnum.ORDER_REFUND_TYPE_0.getCode());
            preTopUp.setStatus(UserConstants.StateEnum.ORDER_STATUS_4.getCode()).setRestAmount(BigDecimal.ZERO);
        } else {
            // 部分退款
            refund.setType(UserConstants.StateEnum.ORDER_REFUND_TYPE_1.getCode());
            // 假如全部退费完了
            if (dto.getAmount().compareTo(preTopUp.getRestAmount()) == 0) {
                // 全额退费
                preTopUp.setStatus(UserConstants.StateEnum.ORDER_STATUS_4.getCode());
            } else {
                preTopUp.setStatus(UserConstants.StateEnum.ORDER_STATUS_5.getCode());
            }
            preTopUp.setRestAmount(preTopUp.getRestAmount().subtract(dto.getAmount()));
        }
        //  更新充值订单信息
        refund.setCreator(SecurityUtils.getUserId());
        preTopUp.setModifier(SecurityUtils.getUserId())
                .setGmtModified(new Date());
        beautyRefundService.save(refund);
        beautyTopUpService.updateById(preTopUp);
        // 更新客户积分
        BeautyMember member = this.getById(dto.getMemberId());
        if (member == null) {
            throw new BusinessException("该客户未存在");
        }
        member.setAmount(member.getAmount().subtract(dto.getAmount()))
                .setModifier(SecurityUtils.getUserId())
                .setGmtModified(new Date());
        this.updateById(member);
        // 更新字表信息
        BeautyMemberItem customerItem = beautyMemberItemService.getOne(new LambdaQueryWrapper<BeautyMemberItem>()
                                                                               .eq(BeautyMemberItem::getMemberId, member.getId())
        );
        if (null == customerItem) {
            throw new BusinessException("该客户不存在");
        }
        customerItem
                .setRefundCount(customerItem.getRefundCount() + 1)
                // 累计消费减去
                .setConsumeAmount(customerItem.getConsumeAmount().subtract(refund.getAmount()))
                // 退费金额加上
                .setRefundAmount(customerItem.getRefundAmount().add(refund.getAmount()))
                .setModifier(SecurityUtils.getUserId())
                .setGmtModified(new Date());
        return beautyMemberItemService.updateById(customerItem);
    }

    @Override
    public boolean frontRegister(BeautyMemeberRegisterDTO dto) {
        BeautyMember member = new BeautyMember();
        BeanUtils.copyProperties(dto, member);
        member.setPassword(SecurityUtils.encryptPassword(dto.getPassword()));
        member.setId(idWorker.nextId());
        // 判断手机号是否唯一
        if (this.checkPhoneUnique(member)) {
            throw new BusinessException("注册会员'" + member.getPhone() + "'失败，手机号码已存在");
        }
        return this.save(member);
    }

    @Override
    public boolean perfectMemberInfo(BeautyMemberModifyDTO dto) {
        BeautyMember member = this.getById(SecurityUtils.getUserId());
        member.setProvince(dto.getProvince()).setCity(dto.getCity()).setCounty(dto.getCounty())
                .setRealName(dto.getRealName())
                .setIdNumber(dto.getIdNumber())
                .setAddress(dto.getAddress())
                .setGmtModified(new Date());
        // 判断身份证号是否唯义
        if (this.checkIdNumberUnique(member)) {
            throw new BusinessException("完善客户信息'" + member.getIdNumber() + "'失败，身份证号码已存在");
        }
        return this.updateById(member);
    }


    private boolean checkPhoneUnique(BeautyMember member) {
        Long customerId = null == member.getId() ? -1L : member.getId();
        BeautyMember one = this.getOne(new LambdaQueryWrapper<BeautyMember>()
                                               .select(BeautyMember::getId, BeautyMember::getPhone)
                                               .eq(BeautyMember::getPhone, member.getPhone())
        );
        // 判断id是否一致 不一致就代表 唯一 false
        return null != one && !customerId.equals(one.getId());
    }


    private boolean checkIdNumberUnique(BeautyMember member) {
        Long customerId = null == member.getId() ? -1L : member.getId();
        BeautyMember one = this.getOne(new LambdaQueryWrapper<BeautyMember>()
                                               .select(BeautyMember::getId, BeautyMember::getIdNumber)
                                               .eq(BeautyMember::getIdNumber, member.getIdNumber())
        );
        // 判断id是否一致 不一致就代表 唯一 false
        return null != one && !customerId.equals(one.getId());
    }


    private BeautyMember getByPhone(String phone) {
        return this.getOne(new LambdaQueryWrapper<BeautyMember>()
                                   .eq(BeautyMember::getIsDeleted, UserConstants.StateEnum.STATUS_NORMAL.getCode())
                                   .eq(BeautyMember::getStatus, UserConstants.StateEnum.STATUS_NORMAL.getCode())
                                   .eq(BeautyMember::getPhone, phone)
        );
    }

    /**
     * 判断手机号已经被使用了
     *
     * @param phone 手机号
     *
     * @return
     */
    private boolean checkPhoneUsed(String phone) {
        BeautyMember one = this.getOne(new LambdaQueryWrapper<BeautyMember>()
                                               .eq(BeautyMember::getIsDeleted, UserConstants.StateEnum.STATUS_NORMAL.getCode())
                                               .eq(BeautyMember::getStatus, UserConstants.StateEnum.STATUS_NORMAL.getCode())
                                               .eq(BeautyMember::getPhone, phone));
        return null != one && StringUtils.checkValNotNull(one.getOpenId());
    }

    /**
     * 检查当前手机号是否已经被绑定过了
     *
     * @param phone 手机号
     *
     * @return
     */
    private boolean checkOpenIdBinded(String phone) {
        BeautyMember one = this.getOne(new LambdaQueryWrapper<BeautyMember>()
                                               .eq(BeautyMember::getPhone, phone)
                                               .eq(BeautyMember::getIsDeleted, UserConstants.StateEnum.STATUS_NORMAL.getCode())
        );
        return one != null && StringUtils.checkValNotNull(one.getOpenId());
    }

    private LambdaQueryWrapper<BeautyMember> getLambdaQueryWrapper(BeautyMemberQueryDTO dto) {
        return new LambdaQueryWrapper<BeautyMember>()
                .eq(BeautyMember::getIsDeleted, UserConstants.StateEnum.DELETE_FLAG_EXIST.getCode())
                .like(StringUtils.checkValNotNull(dto.getPhone()), BeautyMember::getPhone, dto.getPhone())
                .like(StringUtils.checkValNotNull(dto.getIdNumber()), BeautyMember::getIdNumber, dto.getIdNumber())
                .like(StringUtils.checkValNotNull(dto.getRealName()), BeautyMember::getRealName, dto.getRealName())
                .eq(StringUtils.checkValNotNull(dto.getStatus()), BeautyMember::getStatus, dto.getStatus())
                .eq(StringUtils.checkValNotNull(dto.getGender()), BeautyMember::getGender, dto.getGender())
                .like(StringUtils.checkValNotNull(dto.getEmail()), BeautyMember::getEmail, dto.getEmail())
                .like(StringUtils.checkValNotNull(dto.getOpenId()), BeautyMember::getOpenId, dto.getOpenId())
                .between(StringUtils.checkValNotNull(dto.getParams().get(Constants.BEGIN_TIME)), BeautyMember::getGmtCreate, dto.getParams().get(Constants.BEGIN_TIME), dto.getParams().get(Constants.END_TIME))
                ;
    }
}
